package org.codetranslators.common.optimization;

import java.util.Vector;
import java.util.HashMap;
import java.util.Stack;

import org.codetranslators.common.Temp;
import org.codetranslators.compiler.intermrep.AssemblyInstruction;

public class SSAPhiFunction implements AssemblyInstruction{
	private Vector destList;
	private Vector srcList;
	
	private String instructionDescription;
	
	private boolean isMoveStm;
	
	private int numParams;
	private Temp destTemp;
	
	private int currentParamNumberToBeRenamed;   // Represents the curren parameter index that should be renamed
												 // by the renaming alogrithm
	
	public SSAPhiFunction(){
		srcList = new Vector();
		destList = new Vector();
		currentParamNumberToBeRenamed = 0;
	}
	
	/*
	 * This constructor is called when a phi function is first created; in this case,
	 * the destination (the left-hand side of the phi function) is the same as those
	 * on right side (the right-hand side variables). For example, this would result
	 * in phi functions that look like this:
	 * 
	 * $t0 <- phi($t0, $t0);
	 * 
	 */
	
	public SSAPhiFunction(Temp temp, int numPredecessors) {
		srcList = new Vector();
		destList = new Vector();
		numParams = numPredecessors;
		destTemp = temp;
		
		for(int i = 0; i < numPredecessors; i++){
			srcList.addElement(temp);
		}
		
		updateInstructionString();
	
		destList.addElement(temp);
		isMoveStm = true;
	}
	
	/*
	 * This constructor is called when a phi function already exists and we need to create
	 * a new phi function from that phi function. This will therefore need the new destination
	 * variable (the first parameter to the constructor) and the source variables from the old
	 * phi function (the second parameter to the construtor).
	 */
	
	public SSAPhiFunction(Temp temp, Vector srcList, int currentParamNum) {
		this.srcList = srcList;
		destList = new Vector();
		numParams = srcList.size();
		destTemp = temp;
		
		updateInstructionString();
	
		destList.addElement(temp);
		isMoveStm = true;
		currentParamNumberToBeRenamed = currentParamNum;
	}
	
	public int getNumParams(){
		return numParams;
	}
	
	public void updateInstructionString(){
		String paramStr = "";
		String destTempName = destTemp.toString();
		for(int i = 0; i < numParams; i++){
			Temp temp = (Temp) srcList.elementAt(i);
			if(i < numParams -1) paramStr += temp + ", ";
			else paramStr += temp;
		}
		
		instructionDescription = destTempName + " <- phi(" + paramStr + ")"; 
	}
	
	public void updateParamName(HashMap oldNameVsNewSSATemp, int paramNum) {
		/*
		Temp oldTemp = (Temp) srcList.elementAt(paramNum);
		Temp newTemp = (Temp) oldNameVsNewSSATemp.get(oldTemp.toString());
		Temp newTemp = (Temp) oldNameVsNewSSATemp.get(oldTemp.toString());
		if(newTemp != null) {
			srcList.set(paramNum, newTemp);
		}
		updateInstructionString();
		*/
		
		Temp oldTemp = (Temp) srcList.elementAt(currentParamNumberToBeRenamed);
		Stack stack = (Stack) oldNameVsNewSSATemp.get(oldTemp);
		SSATemp newTemp = null;
		
		if(stack != null){
			Integer countInt = (Integer) stack.lastElement();
			int count = countInt.intValue();
			newTemp = new SSATemp(oldTemp, count);
		}
		if(newTemp != null) 
			//srcList.set(currentParamNumberToBeRenamed, newTemp);
			srcList.set(paramNum, newTemp);
		
		updateInstructionString();
		
		currentParamNumberToBeRenamed++;  // Update the parameter index to be renamed
		
		//If the index exceeds the size of the source list, wrap around to zero
		if(currentParamNumberToBeRenamed == srcList.size()){
			currentParamNumberToBeRenamed = 0;
		}
	}
	
	public String getAssemCode(boolean showUseDef) {
		if(!showUseDef)
			return instructionDescription;
		else
		{
			String comment = "				# Def = ";
			
			if(destList == null) comment += "None";
			else
			{
				int numDestTemps = destList.size();
				for(int i = 0; i < numDestTemps; i++)
				{
					comment += (Temp) destList.elementAt(i) + " ";
				}
			}
			
			comment += " Use = ";
			if(srcList == null) comment += "None";
			else
			{
				int numSrcTemps = srcList.size();
				for(int i = 0; i < numSrcTemps; i++)
				{
					comment += (Temp) srcList.elementAt(i) + " ";
				}
			}
			
			// Remove the newline at the end of assemcode
			int nlOffset = instructionDescription.lastIndexOf("\n");
			if(nlOffset > 0)
			{
				instructionDescription = instructionDescription.substring(0, nlOffset);
			}
			
			return instructionDescription + comment + "\n";
		}
	}
	
	public Vector getSrcList() {
		return srcList;
	}
	
	public Vector getDestList(){
		return destList;
	}
	
	public Vector getJumps(){
		return null;
	}
	
	public String getLabelStr(){
		return null;
	}
	
	public int getType()
	{
		return AssemblyInstruction.SSA_PHI_FUNCTION;
	}
	
	public boolean isMove(){
		return isMoveStm;
	}

	public Temp getDestTemp() {
		return destTemp;
	}

	public int getCurrentParamNumberToBeRenamed() {
		return currentParamNumberToBeRenamed;
	}
}
